<!DOCTYPE html><html class="appearance-auto" lang="zh-CN"><head><meta charset="UTF-8"><title>【Spring源码解析系列】 Spring解决循环依赖</title><meta name="description" content="用创业者心态做好每一件事情！"><meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, initial-scale=1"><!-- Google Analytics --><!-- End Google Analytics -->
<!-- Baidu Analytics --><!-- End Baidu Analytics --><link rel="icon" href="/blog/images/favicon.png"><link rel="stylesheet" href="/blog/style/common/bulma.css"><link rel="stylesheet" href="/blog/style/base.css"><link rel="stylesheet" href="/blog/style/common/helper.css"><script src="/blog/js/common.js"></script><link rel="stylesheet" href="/blog/style/post.css"><link rel="stylesheet" href="/blog/style/themes/highlight-theme-light.css"><script src="/blog/js/highlight.pack.js"></script><meta name="description" content="
本文主要讲述Spring通过三级缓存解决循环依赖问题


流程图
循环依赖
什么是循环依赖？存在类A实例Bean、类B实例Bean、类C实例Bean，在类A中引用了类B，类B中引用了类C，同时类C中引用了类A，此时使用Spring初始化类A、B、C便会存在循环引用的情况。当实例化A时发现其引用了B实例；于时对B进行实例化，实例化B时又对C实例进行引用；于时再实例化C，实例化C时，又引用了A实例，而此时A还在等待B初始化完成，因此出现死循环的现象。

循环依赖解决DefaultSingletonBeanRegistry

/** Cache of singleton objects: bean name to bean instance. */
//一级缓存：singletonObjects，用于缓存单例对.."><!-- hexo injector head_end start -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css">

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/hexo-math@4.0.0/dist/style.css">
<!-- hexo injector head_end end --><meta name="generator" content="Hexo 5.4.1"><link rel="alternate" href="/blog/atom.xml" title="janedler's blog" type="application/atom+xml">
</head><body class="is-flex is-flex-direction-column"><header class="header-widget is-flex-shrink-0 is-hidden-mobile"><div class="container is-fullhd is-flex is-justify-content-space-between is-align-items-center is-full-height"><section class="is-hidden-mobile is-flex-shrink-0"><h2><a href="/blog/">田园牧歌(*︶*)'s blog</a></h2></section><h3 class="is-hidden-mobile is-family-serif is-full-height is-flex is-align-items-center is-flex-shrink-0"><div class="is-full-height" id="postTopic"><p class="is-full-height is-flex-shrink-0 is-flex is-align-items-center is-justify-content-center">【Spring源码解析系列】 Spring解决循环依赖</p><p class="is-full-height is-flex-shrink-0 is-flex is-align-items-center is-justify-content-center">点击返回顶部</p></div></h3><aside class="is-flex-shrink-0"><h3 class="is-inline-block"><a href="/blog/">首页</a></h3><h3 class="is-inline-block"><a href="/blog/about">关于</a></h3><h3 class="is-inline-block"><a href="/blog/archives">归档</a></h3></aside></div></header><header class="is-flex header-widget is-flex-shrink-0 is-align-items-center is-justify-content-center is-hidden-tablet"><h3 class="is-inline-block"><a href="/blog/">首页</a></h3><h3 class="is-inline-block"><a href="/blog/about">关于</a></h3><h3 class="is-inline-block"><a href="/blog/archives">归档</a></h3></header><main><main class="container is-max-widescreen content section post-page pt-4 px-4"><div class="columns is-flex-desktop is-justify-content-center is-flex-direction-row-reverse"><div class="column is-3 is-hidden-mobile"><ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%B5%81%E7%A8%8B%E5%9B%BE"><span class="toc-text">流程图</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96"><span class="toc-text">循环依赖</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96%E8%A7%A3%E5%86%B3"><span class="toc-text">循环依赖解决</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BC%93%E5%AD%98%E6%B7%BB%E5%8A%A0%E8%BF%87%E7%A8%8B"><span class="toc-text">缓存添加过程</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BC%93%E5%AD%98%E8%8E%B7%E5%8F%96%E8%BF%87%E7%A8%8B"><span class="toc-text">缓存获取过程</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%B8%89%E7%BA%A7%E7%BC%93%E5%AD%98%E8%A7%A3%E5%86%B3%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96%E6%B5%81%E7%A8%8B"><span class="toc-text">三级缓存解决循环依赖流程</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%80%BB%E7%BB%93"><span class="toc-text">总结</span></a></li></ol></div><div class="column is-9"><header class="my-4"><a href="/blog/tags/Java"><i class="tag post-item-tag">Java</i></a><a href="/blog/tags/Spring"><i class="tag post-item-tag">Spring</i></a></header><h1 class="mt-0 mb-1 is-family-serif" id="postTitle">【Spring源码解析系列】 Spring解决循环依赖</h1><time class="has-text-grey" datetime="2022-11-28T16:00:00.000Z">2022-11-29</time><article class="mt-2 post-content"><p><img src="https://janedler.oss-cn-beijing.aliyuncs.com/images/spring.png" alt="cover"></p>
<p>本文主要讲述Spring通过三级缓存解决循环依赖问题</p>
<span id="more"></span>

<h2 id="流程图"><a href="#流程图" class="headerlink" title="流程图"></a>流程图</h2><p><img src="https://janedler.oss-cn-beijing.aliyuncs.com/images/202211291607415.png" alt="img"></p>
<h2 id="循环依赖"><a href="#循环依赖" class="headerlink" title="循环依赖"></a>循环依赖</h2><blockquote>
<p>什么是循环依赖？<br>存在类A实例Bean、类B实例Bean、类C实例Bean，在类A中引用了类B，类B中引用了类C，同时类C中引用了类A，此时使用Spring初始化类A、B、C便会存在循环引用的情况。当实例化A时发现其引用了B实例；于时对B进行实例化，实例化B时又对C实例进行引用；于时再实例化C，实例化C时，又引用了A实例，而此时A还在等待B初始化完成，因此出现死循环的现象。</p>
</blockquote>
<h2 id="循环依赖解决"><a href="#循环依赖解决" class="headerlink" title="循环依赖解决"></a>循环依赖解决</h2><pre><code class="java">DefaultSingletonBeanRegistry

/** Cache of singleton objects: bean name to bean instance. */
//一级缓存：singletonObjects，用于缓存单例对象实例，这里存放的是已经实例完的对象Bean
private final Map&lt;String, Object&gt; singletonObjects = new ConcurrentHashMap&lt;&gt;(256);

/** Cache of early singleton objects: bean name to bean instance. */
//二级缓存：earlySingletonObjects，用于存放早期实例的单例对象，这里存放的是正在创建实例，且还没实例完成的Bean
private final Map&lt;String, Object&gt; earlySingletonObjects = new ConcurrentHashMap&lt;&gt;(16);

/** Cache of singleton factories: bean name to ObjectFactory. */
//三级缓存：singletonFactories，用于缓存单例工厂
private final Map&lt;String, ObjectFactory&lt;?&gt;&gt; singletonFactories = new HashMap&lt;&gt;(16);
</code></pre>
<pre><code class="java">@Override
public Object getBean(String name) throws BeansException &#123;
   //doGetBean才是真正向IOC容器获取被管理Bean的过程
   return doGetBean(name, null, null, false);
&#125;
</code></pre>
<pre><code class="java">AbstractBeanFactory

//真正实现向IOC容器获取Bean的功能，也是触发依赖注入功能的地方
//如果Bean定义的单例模式（Singleton）,则容器在创建之前先从缓存中查找，以确保整个容器中只存在一个实例对象。
//如果Bean定义的是原型模式（Prototype），则容器每次都会创建一个新的实例对象。
//此源码只是定义了根据Bean定义的模式，采取不同的创建Bean实例对象的策略，具体的Bean实例对象的创建过程
//由实现了ObjectFactory接口的匿名内部类的createBean方法完成，ObjectFactory使用了委派模式，具体
//的Bean实例创建过程交由其实现类AbstractAutowireCapableBeanFactory完成。
@SuppressWarnings(&quot;unchecked&quot;)
protected &lt;T&gt; T doGetBean(
      String name, @Nullable Class&lt;T&gt; requiredType, @Nullable Object[] args, boolean typeCheckOnly)
      throws BeansException &#123;
   //先从缓存中取是否已经有被创建过的单态类型的Bean
   //对于单例模式的Bean整个IOC容器中只创建一次，不需要重复创建
   //getSingleton涉及到解决循环依赖问题
   Object sharedInstance = getSingleton(beanName);
   ...代码省略...
   // Create bean instance.
   //创建单例模式Bean的实例对象
   if (mbd.isSingleton()) &#123;
           //这里使用了一个匿名内部类，创建Bean实例对象，并且注册给所依赖的对象
        sharedInstance = getSingleton(beanName, () -&gt; &#123;
            try &#123;
               //创建一个指定Bean实例对象，如果有父级继承，则合并子类和父类
               return createBean(beanName, mbd, args);
            &#125;
            catch (BeansException ex) &#123;
                //显示的从容器单例模式Bean缓存中清除实例对象
                destroySingleton(beanName);
                throw ex;
            &#125;
         &#125;);
        //获取给定Bean实例对象
        beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
  &#125;
  //IOC容器创建原型模式Bean实例对象
 else if (mbd.isPrototype()) &#123;
            ...代码省略...
 &#125;
...代码省略...
   return adaptBeanInstance(name, beanInstance, requiredType);
&#125;
</code></pre>
<h2 id="缓存添加过程"><a href="#缓存添加过程" class="headerlink" title="缓存添加过程"></a>缓存添加过程</h2><p>缓存添加过程主要发生在创建Bean也就是doCreateBean的过程</p>
<pre><code class="java">createBean -&gt; doCreateBean

//正在创建Bean的方法
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException &#123;
   ...代码省略...
   if (instanceWrapper == null) &#123;
      //创建实例对象
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   &#125;
     ...代码省略...
   //向容器中缓存单例模式的Bean对象，以防循环引用
   boolean earlySingletonExposure = (mbd.isSingleton() &amp;&amp; this.allowCircularReferences &amp;&amp;
         isSingletonCurrentlyInCreation(beanName));
   if (earlySingletonExposure) 
      //这里是一个匿名内部类，为了防止循环引用，尽早持有对象的引用
      addSingletonFactory(beanName, () -&gt; getEarlyBeanReference(beanName, mbd, bean));
   &#125;

   // Initialize the bean instance.
   //Bean对象初始化，依赖注入在此触发
   //这个exposedObject在初始化完成之后返回作为依赖注入完成后的Bean
   Object exposedObject = bean;
   try &#123;
      //将Bean实例对象封装，并且Bean定义中配置的属性值赋值给实例对象
      populateBean(beanName, mbd, instanceWrapper);
      //初始化Bean对象
      //在对Bean实例对象生成和依赖注入完成以后，开始对Bean实例对象
      //进行初始化，为Bean实例对象应用BeanPostProcessor后置处理器
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   &#125;
  ...代码省略...
   return exposedObject;
&#125;
</code></pre>
<p>将beanName放入三级缓存中，并且从二级缓存中移除</p>
<pre><code class="java">protected void addSingletonFactory(String beanName, ObjectFactory&lt;?&gt; singletonFactory) &#123;
   Assert.notNull(singletonFactory, &quot;Singleton factory must not be null&quot;);
   synchronized (this.singletonObjects) &#123;
      //一级缓存singletonObjects里面找到bean
      if (!this.singletonObjects.containsKey(beanName)) &#123;
         //三级缓存singletonFactories保存
         this.singletonFactories.put(beanName, singletonFactory);
         //从二级缓存earlySingletonObjects移除
         this.earlySingletonObjects.remove(beanName);
         this.registeredSingletons.add(beanName);
      &#125;
   &#125;
&#125;
</code></pre>
<p>在sharedInstance = getSingleton(beanName, () -&gt; {});调用完成后会调用addSingleton()，实例从二级、三级缓存中移除，只保存在一级缓存中。</p>
<pre><code class="java">protected void addSingleton(String beanName, Object singletonObject) &#123;
   synchronized (this.singletonObjects) &#123;
      //保存到singletonObjects缓存里
      this.singletonObjects.put(beanName, singletonObject);
      //从singletonFactories工厂缓存中移除相应的bean的实例
      this.singletonFactories.remove(beanName);
      //从早期单例缓存中移除对应的bean实例
      this.earlySingletonObjects.remove(beanName);
      //将该bean添加注册表中
      this.registeredSingletons.add(beanName);
   &#125;
&#125;
</code></pre>
<h2 id="缓存获取过程"><a href="#缓存获取过程" class="headerlink" title="缓存获取过程"></a>缓存获取过程</h2><p>DefaultSingletonBeanRegistry::getSingleton (解决循环依赖关键代码)</p>
<pre><code class="java">//获取单例对象
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) &#123;
   // Quick check for existing instance without full singleton lock
   // 检查singletonObjects中是否能获取到
   Object singletonObject = this.singletonObjects.get(beanName);
   //如果为null且当前的单例的bean正在创建过程中
   if (singletonObject == null &amp;&amp; isSingletonCurrentlyInCreation(beanName)) &#123;
      //从早期单例bean缓存中去获取
      singletonObject = this.earlySingletonObjects.get(beanName);
      //如果为null,且提前创建
      if (singletonObject == null &amp;&amp; allowEarlyReference) &#123;
         //锁定该全局变量singletonObjects进行相关的处理
         synchronized (this.singletonObjects) &#123;
            // Consistent creation of early reference within full singleton lock
            //从单例工厂中获取
            singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) &#123;
               singletonObject = this.earlySingletonObjects.get(beanName);
               if (singletonObject == null) &#123;
                  ObjectFactory&lt;?&gt; singletonFactory = this.singletonFactories.get(beanName);
                  if (singletonFactory != null) &#123;
                     //调用getObject并获取对应的实例
                     singletonObject = singletonFactory.getObject();
                     //保存到earlySingletonObjects缓存中
                     this.earlySingletonObjects.put(beanName, singletonObject);
                     //同时移除singletonFactories中的对应的objectFactory
                     this.singletonFactories.remove(beanName);
                  &#125;
               &#125;
            &#125;
         &#125;
      &#125;
   &#125;
   //如果从singletonObjects获取到了直接返回，不重复创建
   return singletonObject;
&#125;
</code></pre>
<blockquote>
<p>获取过程<br>1、从一级缓存singletonObjects中获取bean;<br>2、如果没有获取到，且当前的单例的bean正在创建过程中，则从二级缓存earlySingletonObjects中获取；singletonObject == null &amp;&amp; isSingletonCurrentlyInCreation(beanName)<br>3、如果二级缓存获取失败，且允许从三级缓存singletonFactories中获取，则从三级缓存获取。singletonObject == null &amp;&amp; allowEarlyReference<br>4、锁定该全局变量singletonObjects进行相关的处理；synchronized (this.singletonObjects)；<br>5、在同步模块下，依次从一级缓存singletonObjects、二级缓存earlySingletonObjects、三级缓存singletonFactories中获取；<br>6、如果从三级缓存中获取到了，缓存进行升级，删除三级缓存bean，保存到二级缓存。</p>
</blockquote>
<p>关于三级缓存总结<br>在doGetBean方法里面调用getSingleton获取单例实例的时候，如果实例正在创建、允许提前获取还未创建的bean，并且一级、二级缓存都为空，三级缓存不为空的前提下，删除三级缓存bean，二级缓存进行缓存bean。<br>如果getSingleton并未获取到缓存实例，通过调用getSingleton(beanName, () -&gt; {return createBean(beanName, mbd, args) }创建bean实例，实例从二级、三级缓存中移除，只保存在一级缓存中。</p>
<h2 id="三级缓存解决循环依赖流程"><a href="#三级缓存解决循环依赖流程" class="headerlink" title="三级缓存解决循环依赖流程"></a>三级缓存解决循环依赖流程</h2><p>通过使用三级缓存可以解决循环依赖的问题，以A、B、C相互依赖的情况为例，其解决循环依赖流程如下：<br>1、创建A对象，执行createInstance之后将对象放入三级缓存singletonFactories中提前曝光自己，并继续执行<br>2、执行populateBean为A对象填充属性，此时发现A依赖了B对象，于是通过getBean获取B对象，此时B还没创建，便会执行创建B的Bean对象流程<br>3、执行createInstance创建B对象，并放入三级缓存singletonFactories提前曝光<br>4、执行populateBean为B对象填充属性时，发现B对象依赖了C对象，于是getBean获取C对象<br>5、此时C对象未创建，于是执行创建C对象Bean过程，在填充C时发现C依赖A对象，于是通过getBean获取对象A<br>6、此时A对象并未初始化完成，仅仅是放入三级缓存中，在getSingleton时从三级缓存中取出A的ObjecFactory来获取对象，并且将A对象进行缓存升级，从二级缓存放入三级缓存，此时C也拿到了A对象实例<br>7、A缓存从三级升到了二级，在Bean创建完成之后会通过addSingleton将缓存从二级再升到三级中。B、C流程相同<br><img src="https://janedler.oss-cn-beijing.aliyuncs.com/images/202211291649240.png" alt="img"></p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>1、当两个Bean相互引用依赖的情况称为循环引用，Spring使用三级缓存的方式解决循环依赖<br>2、循环依赖发生在创建Bean的过程中，具体发生在doCreateBean方法中调用createBeanInstance和populateBean方法时<br>3、由于在createBeanInstance时用构造方法提前曝光实例，之后再将实例放入三级缓存中，因此Spring不能解决构造函数中的循环依赖问题<br>4、在使用getSingleton获取Bean实例时，如果对象在三级缓存中，则会从三级缓存移除添加到二级缓存；如果在二级缓存中，则会从二级缓存中移除添加到一级缓存中，以达到缓存升级的目的<br>5、在三个级别缓存中，二级缓存和三级缓存从代码上看是互斥的，实例对象不能同时存在于二三级缓存当中</p>
</article><section class="jump-container is-flex is-justify-content-space-between my-6"><!-- em is empty placeholder--><a class="button is-default" href="/blog/2022/11/29/Spring/Spring%E6%89%A9%E5%B1%95%E5%8A%9F%E8%83%BD-ApplicationListener/" title="【Spring源码解析系列】 ApplicationListener"><i class="iconfont icon-prev mr-2 has-text-grey"></i><span class="has-text-weight-semibold">上一页: 【Spring源码解析系列】 ApplicationListener</span></a><a class="button is-default" href="/blog/2022/11/29/Spring/Spring%E5%AE%B9%E5%99%A8%E5%88%9D%E5%A7%8B%E5%8C%96/" title="【Spring源码解析系列】 Spring容器初始化"><span class="has-text-weight-semibold">下一页: 【Spring源码解析系列】 Spring容器初始化</span><i class="iconfont icon-next ml-2 has-text-grey"></i></a></section><article class="mt-6 comment-container"><script async repo="janedler/blog" src="https://utteranc.es/client.js" issue-term="pathname" theme="preferred-color-scheme"></script></article></div></div></main></main><footer class="is-flex is-flex-direction-column is-align-items-center is-flex-shrink-0 is-family-serif"><section class="sns-container"><!-- Github--><a title="github" target="_blank" rel="noopener nofollow" href="//github.com//janedler"><i class="iconfont icon-github"></i></a><!-- Ins--><!-- RSS--><a title="rss" target="_blank" rel="noopener nofollow" href="/blog/atom.xml"><i class="iconfont icon-rss"></i></a><!-- 知乎--><!-- 领英--><!-- 脸书--></section><p><span>Copyright ©</span><span> 田园牧歌(*︶*) 2022</span></p><div class="is-flex is-justify-content-center is-flex-wrap-wrap"><p>Powered by Hexo &verbar;&nbsp;</p><p class="is-flex is-justify-content-center"><a title="Hexo theme author" target="_blank" rel="noopener" href="//github.com/haojen">Theme by Haojen&nbsp;</a></p><div style="margin-top: 2px"><a class="github-button" title="github-button" target="_blank" rel="noopener" href="https://github.com/haojen/hexo-theme-Claudia" data-color-scheme="no-preference: light; light: light; dark: dark;" data-show-count="true"></a></div></div><div><span></span></div></footer><script async defer src="https://buttons.github.io/buttons.js"></script><script src="/blog/js/post.js"></script></body></html>